package pers.chao.cacheable.core;

import cn.hutool.core.collection.CollUtil;
import pers.chao.cacheable.core.annotation.CacheZSet;
import pers.chao.cacheable.infrastructure.ErrorDesc;
import pers.chao.cacheable.infrastructure.utils.ElementUtil;
import pers.chao.cacheable.infrastructure.utils.RedisOp;
import pers.chao.tool.infrastructure.FuncInterface;
import pers.chao.tool.spring.SpringElUtil;
import pers.chao.tool.validate.ObjValidator;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Description: zset缓存操作
 *
 * @author W.Chao
 * @date 2020/10/2 14:35
 **/
public class CacheZSetProcessor extends AbstractCacheProcessor {
	private CacheZSet annotation;

	private static final FuncInterface.T2R<Set, Boolean> TYPE_ASSERT = (set) -> {
		for (Object o : set) {
			return o instanceof String;
		}
		return false;
	};

	public CacheZSetProcessor(CacheZSet annotation) {
		this.annotation = annotation;
	}

	@Override
	protected void preInfo() {
		logger.info("[Cache sorted set previous INFO] @cache -> {}, @cacheZSet -> {}", cacheConditionInfo(), annotation);
	}

	@Override
	public void parseRedisStructKeyOrOther() {

	}

	@Override
	public Object preQuery() {
		if (RedisOp.hasKey(finalKey)) {
			boolean asc = annotation.asc();
			if (annotation.withScore()) {
				return RedisOp.zRangeByScoreWithScores(finalKey, Double.MIN_VALUE, Double.MAX_VALUE, Integer.MAX_VALUE, asc);
			}
			return RedisOp.zRange(finalKey, 0, -1, asc);
		}
		return null;
	}

	@Override
	@SuppressWarnings("all")
	public void afterMethodOperate(Object o) {
		String evictElement = annotation.evictElement();
		boolean isReplaceAll = annotation.replaceAll();

		if (evict) {
			RedisOp.del(finalKey);
			return;
		} else if (ObjValidator.isNotEmptyString(evictElement)) {
			ArrayList removedElements = SpringElUtil.parseSpringEL(method, new ArrayList<>(params.values()).toArray(), evictElement, ArrayList.class, CollUtil.newArrayList(evictElement));
			// 删除多个元素
			List finalRemoved = ElementUtil.transferElementType(removedElements, annotation.eqValueType());
			RedisOp.zRemove(finalKey, finalRemoved);
			return;
		}

		// 整体替换时预先清除所有键
		if (isReplaceAll) {
			Set<Object> allItem = RedisOp.zRange(finalKey, 0, -1, true);
			RedisOp.zRemove(finalKey, allItem);
		}

		if (o instanceof Map) {
			Map<Object, Double> valueAndScore = (Map<Object, Double>) o;
			Map<String, Double> stringKeyZset = null;

			// 只支持Stirng的键
			Boolean keyTypeIsString = TYPE_ASSERT.apply(valueAndScore.keySet());
			if (!keyTypeIsString) {
				throw new CacheableException(ErrorDesc.MAP_KEY_TYPE_ERROR, valueAndScore.keySet().getClass());
			}

			stringKeyZset = (Map<String, Double>) o;
			// 数值检查
			for (Map.Entry<String, Double> next : stringKeyZset.entrySet()) {
				if (next.getValue() == null) {
					next.setValue(0.0);
				}
			}

			boolean isFirst = !RedisOp.hasKey(finalKey);
			if (isFirst && isNeedExpire) {
				RedisOp.zAdd(finalKey, stringKeyZset, expire, unit);
			} else {
				RedisOp.zAdd(finalKey, stringKeyZset);
			}
		} else {
			throw new CacheableException(ErrorDesc.ZSET_ITEM_TYPE_ERROR, o.getClass());
		}
	}
}
